﻿using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Discovery;
using Nardax.ServiceModel.Discovery.Bindings;

namespace Nardax.ServiceModel.Discovery
{
    internal class ServiceProxy<TServiceContract> where TServiceContract : class
    {
        private readonly EndpointDiscoveryMetadata _metadata;
        private TServiceContract _channel;

        public ServiceProxy(EndpointDiscoveryMetadata metadata)
        {
            _metadata = metadata;
        }

        public void Call(Action<TServiceContract> method)
        {
            method(GetChannel());
        }

        public TResult Call<TResult>(Func<TServiceContract, TResult> method)
        {
            var result = method(GetChannel());
            return result;
        }

        private TServiceContract GetChannel()
        {
            if (_channel == null)
            {
                var binding = GetBinding();
                _channel = ChannelFactory<TServiceContract>.CreateChannel(binding, _metadata.Address);
            }

            return _channel;
        }

        private Binding GetBinding()
        {
            // first look to see if I can find a client configuration for this type and if it has a binding
            // can't find the client or found it and it doesn't have a binding set up so I will check for just a binding
            // can't even find a binding so I am just going to create a default binding using the address
            return GetBindingFromMetadata() ?? GetDefaultBinding();
        }

        private Binding GetBindingFromMetadata()
        {
            var bindingElement = (from e in _metadata.Extensions where e.Name == BindingSerializer.RootElementName select e).FirstOrDefault();

            if (bindingElement != null)
            {
                return BindingSerializer.Deserialize(bindingElement);
            }

            return null;
        }

        private Binding GetDefaultBinding()
        {
            switch (_metadata.Address.Uri.Scheme)
            {
                case "net.tcp":
                    return new NetTcpBinding();
                case "http":
                    return  new WSHttpBinding();
            }

            throw new NotSupportedException(String.Format("The scheme '{0}' is not support.", _metadata.Address.Uri.Scheme));
        }

        public EndpointAddress Address
        {
            get { return _metadata.Address; }
        }
    }
}
